﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace _404park.Utilities.Extensions
{
    public class IfClause<T>
    {
        public T Result { get; set; }

        public IfClause<T> Clause(Func<T> func)
        {
            if (Result.Equals(default(T)) && func != null)
                Result = func();

            return this;
        }
    }

    public class If<T>
    {
        private SortedList<int, Func<T>> _funcs;
        public Action<Exception> LogException { get; set; }
        public bool SkipException { get; set; }

        public If()
            : this(false, null)
        { }

        public If(bool SkipException)
            : this(SkipException, null)
        { }

        public If(bool SkipException, Action<Exception> LogException)
        {
            _funcs = new SortedList<int, Func<T>>();
            this.LogException = LogException;
            this.SkipException = SkipException;
        }

        public void Clause(Func<T> func)
        {
            var maxKey = _funcs.Count == 0 ? 0 : _funcs.Keys.Max();
            Clause(maxKey + 1, func);
        }

        public void Clause(int priority, Func<T> func)
        {
            _funcs.Add(priority, func);
        }

        public T Execute()
        {
            foreach (var func in _funcs)
            {
                var result = default(T);

                try
                {
                    result = func.Value();
                }
                catch (Exception ex)
                {
                    if (LogException != null)
                        LogException(ex);

                    if (!SkipException)
                        throw ex;
                }

                if (result != null)
                    return result;
            }

            return default(T);
        }
    }

    public class IfObject<T, TResult>
        where T : class
        where TResult : class
    {
        private Queue<T> _objects = new Queue<T>();

        public string ExecutionMethod { get; set; }

        public void Clause(T obj)
        {
            if (!obj.Equals(default(T)))
                _objects.Enqueue(obj);
        }

        public void Clear()
        {
            _objects.Clear();
        }

        public TResult Execute()
        {
            TResult result = default(TResult);

            while (result == null && !_objects.Peek().Equals(default(T)))
            {
                var obj = _objects.Dequeue();

                var method = typeof(T).GetMethod(ExecutionMethod);
                result = method.Invoke(obj, null) as TResult;
            }

            return result;
        }
    }

    public class ConditionObject<T, TResult>
    {
        private Func<T, TResult> _pointer;
        public Queue<Execution<T, TResult>> _executions = new Queue<Execution<T, TResult>>();

        public ConditionObject(Func<T, TResult> f)
        {
            _pointer = f;
        }

        public void Clause(T t)
        {
            if (t != null)
                _executions.Enqueue(new Execution<T, TResult>(t, _pointer));
        }

        public void Clear()
        {
            _executions.Clear();
        }

        public TResult Execute()
        {
            TResult result = default(TResult);

            while (result == null && _executions.Count > 0 && !_executions.Peek().Equals(default(T)))
            {
                var execution = _executions.Dequeue();
                result = execution.Func(execution.Object);
            }

            return result;
        }
    }


    public class Execution<T, TResult>
    {
        public T Object;
        public Func<T, TResult> Func;
        public Execution(T o, Func<T, TResult> f)
        {
            Object = o;
            Func = f;
        }
    }

}
